package cn.edu.gdu.quizpractice.controller.web;

import cn.edu.gdu.quizpractice.entity.ApiResult;
import cn.edu.gdu.quizpractice.entity.Manager;
import cn.edu.gdu.quizpractice.service.IEmailService;
import cn.edu.gdu.quizpractice.service.IManagerService;
import cn.edu.gdu.quizpractice.service.OptimizedCounterService;
import cn.edu.gdu.quizpractice.service.IRedisCacheService;
import cn.edu.gdu.quizpractice.common.utils.JwtUtils;
import io.micrometer.core.annotation.Timed;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: Aubery
 * @Date: 2025-01-19-15:27
 * @Description:
 */

@RestController
@RequestMapping("/web/managers")
@Timed(value = "web/managerController",description = "ManagerController")
@Validated      //参数校验
@Transactional
@Slf4j
public class ManagerController {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private IManagerService managerService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private OptimizedCounterService optimizedCounterService;

    @Autowired
    private IEmailService emailService;

    @Autowired
    private IRedisCacheService redisCacheService;

    private static final long ONLINE_USER_EXPIRE_SECONDS = 86400; // 在线用户过期时间:24小时

    @Value("${admin.email}")
    private String adminEmail;

    @Value("${admin.defaultPassword}")
    private String defaultPassword;

    /*@PostMapping("/login")
    public ApiResult login(@Pattern(regexp = "^(?:(?:\\+|00)86)?1\\d{10}$", message = "电话号码格式不正确") String telephone,
                           @Pattern(regexp = "^\\S*(?=\\S{6,})(?=\\S*\\d)(?=\\S*[A-Z])(?=\\S*[a-z])(?=\\S*[!@#$%^&*? ,.])\\S*$",message = "密码格式不正确") String password) {
        // 进行认证
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(telephone, password)
        );

        // 获取UserDetails对象
        UserDetails userDetails = userDetailsService.loadUserByUsername(telephone);

        // 生成token
        String token = jwtUtils.generateToken(userDetails);

        // 检查Redis中是否已有token
        String existingToken = stringRedisTemplate.opsForValue().get(userDetails.getUsername());
        System.out.println("登录前Redis中的Token: " + existingToken);

        // 存储到Redis
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.set(
                userDetails.getUsername(),
                token, // 确保只存储新token，不要拼接
                ONLINE_USER_EXPIRE_SECONDS,
                TimeUnit.SECONDS
        );

        // 验证存储是否正确
        String storedToken = stringRedisTemplate.opsForValue().get(userDetails.getUsername());
        System.out.println("存储后Redis中的Token: " + storedToken);

        optimizedCounterService.incrementWithExpire(
                "online_users",
                ONLINE_USER_EXPIRE_SECONDS
        );

        // 返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        result.put("user", userDetails);

        return ApiResult.success(result);
    }*/

    @PostMapping("/login")
    public ApiResult login(
            @Pattern(regexp = "^(?:(?:\\+|00)86)?1\\d{10}$", message = "电话号码格式不正确") String telephone,
            @Pattern(regexp = "^\\S*(?=\\S{6,})(?=\\S*\\d)(?=\\S*[A-Z])(?=\\S*[a-z])(?=\\S*[!@#$%^&*? ,.])\\S*$", message = "密码格式不正确") String password) {

        // 1. 执行密码认证
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(telephone, password)
        );

        // 2. 直接从认证对象中获取用户信息（无需再次查询数据库）
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        log.info("用户信息：{}", userDetails);

        // 3. 生成 Token
        String token = jwtUtils.generateToken(userDetails);

        // 4. Redis 操作
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        String existingToken = operations.get(userDetails.getUsername());
        System.out.println("登录前Redis中的Token: " + existingToken);

        operations.set(
                userDetails.getUsername(),
                token,
                ONLINE_USER_EXPIRE_SECONDS,
                TimeUnit.SECONDS
        );

        // 5. 返回结果（保持与之前一致）
        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        result.put("user", userDetails);

        return ApiResult.success(result);
    }

    /**
     * 登录后右上角显示用户信息
     * 测试的时候记得在Postman中设置Authorization为Bearer Token，然后输入token即可
     * @return 登录后的用户个人信息
     */
    @GetMapping("/profile")
    public ApiResult getCurrentManager() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null || !authentication.isAuthenticated()) {
            return ApiResult.fail("用户未登录");
        }

        Object principal = authentication.getPrincipal();

        if (principal instanceof Manager) {
            return ApiResult.success((Manager) principal);
        }

        if (principal instanceof UserDetails) {
            String username = ((UserDetails) principal).getUsername();
            Optional<Manager> managerOptional = managerService.findByTele(username);
            return managerOptional.map(ApiResult::success).orElseGet(() -> ApiResult.fail("用户信息查询失败"));
        }

        return ApiResult.fail("无法识别的用户类型");
    }

    @GetMapping("/findAll")
    public ApiResult findAll() {
        // 1. 定义缓存key
        String cacheKey = "managers:all";

        // 2. 尝试从缓存获取
        Object cachedManagers = redisCacheService.get(cacheKey);
        if (cachedManagers != null) {
            System.out.println("从缓存获取所有管理员");
            return ApiResult.success(cachedManagers);
        }

        // 3. 缓存未命中，查询数据库
        System.out.println("缓存未命中，从数据库获取所有管理员");
        List<Manager> all = managerService.findAll();

        // 4. 将结果存入缓存（设置5分钟过期时间）
        if (all != null) {
            // 创建一个简化版的管理员列表，不包含authorities
            List<Map<String, Object>> simplifiedManagers = new ArrayList<>();
            for (Manager manager : all) {
                simplifiedManagers.add(simplifyManager(manager));
            }

            // 缓存简化版的列表
            redisCacheService.set(cacheKey, simplifiedManagers, 5, TimeUnit.MINUTES);
            return ApiResult.success(all); // 返回原始列表
        } else {
            return ApiResult.fail("查询失败");
        }
    }

    /**
     * 添加管理员
     *
     * @param manager
     * @return
     * @description 需要校验用户信息，根据Manager上的正则表达式校验，同时需要标注@Valid注解
     */
    @PostMapping("/add")
    public ApiResult regester(@Valid @RequestBody Manager manager) {
        System.out.println("添加的manager:" + manager);
        Optional<Manager> m = managerService.findByTele(manager.getTelephone());
        System.out.println("查找到的manager:" + m);
        if (m.isEmpty()) {
            manager.setPassword(passwordEncoder.encode(defaultPassword));
            if (managerService.save(manager)) {
                // 删除管理员列表缓存，下次查询时会重新加载
                redisCacheService.delete("managers:all");
                return ApiResult.success("添加成功");
            }
        }
        return ApiResult.fail("添加失败");
    }


    /**
     * 修改管理员信息
     *
     * @param manager
     * @return
     */
    @PutMapping("/update")
    public ApiResult update(@Valid @RequestBody Manager manager/*, @RequestHeader("Authorization") String token*/) {
        Optional<Manager> byTele = managerService.findByTele(manager.getTelephone());
        String password = byTele.get().getPassword();
        manager.setPassword(password);
        System.out.println("修改的manager:" + manager);
        if (managerService.updateByTele(manager)) {
            // 清除管理员列表缓存和对应管理员的缓存
            redisCacheService.delete("managers:all");
            redisCacheService.delete("manager:tele:" + manager.getTelephone());
            return ApiResult.success("修改成功");
        } else {
            return ApiResult.fail("修改失败");
        }
    }

    @PostMapping("/updateEmail")
    public ApiResult updateEmail(@RequestParam String email, @RequestHeader("Authorization") String token) {
        try {
            // 从token中解析出telephone
            String getCurrentManager = jwtUtils.getUsernameFromToken(token);
            if (managerService.updateEmail(getCurrentManager, email))
                return ApiResult.success("修改成功");
            else
                return ApiResult.fail("修改失败");
        } catch (IllegalArgumentException e) {
            return ApiResult.fail("Token无效: " + e.getMessage());
        }
    }

    /**
     * 更新管理员密码
     *
     * @param params
     * @return
     */
    @PostMapping("/updatePwd")
    public ApiResult updatePwd(@RequestBody Map<String, String> params,@RequestHeader("Authorization") String token) {

        System.out.println("接收到的参数:");
        params.forEach((k, v) -> System.out.println(k + ": " + v));
        System.out.println("Token: " + token);

        //获取原密码
        String oldPwd = params.get("old_pwd");
        //获取新密码
        String newPwd = params.get("new_pwd");
        //获取确认的密码
        String confirmPwd = params.get("confirm_pwd");
        if (!StringUtils.hasLength(oldPwd) || !StringUtils.hasLength(newPwd) || !StringUtils.hasLength(confirmPwd)) {
            return ApiResult.fail("密码不能为空");
        }
        Manager manager = (Manager) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (!passwordEncoder.matches(oldPwd, managerService.findPwdByTele(manager.getTelephone()))) {
            return ApiResult.fail("原密码错误");
        } else if (!newPwd.equals(confirmPwd)) {
            return ApiResult.fail("两次密码不一致");
        } else {
            manager.setPassword(passwordEncoder.encode(newPwd));
            managerService.updatePwd(manager.getTelephone(), passwordEncoder.encode(newPwd));
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            operations.getOperations().delete(token);
            optimizedCounterService.decrement("online_users");
            return ApiResult.success("修改成功");
        }
    }

    /**
     * 删除管理员
     * 需要超级管理员权限
     *
     * @param telephone
     * @return
     */
    @PreAuthorize("hasAuthority('ROLE_SUPER_ADMIN')")
    @DeleteMapping("/deleteManager")
    public ApiResult deleteManager(@RequestParam("telephone") String telephone, @RequestHeader("Authorization") String token) {
        // 从token种解析出telephone
        String getCurrentManager = jwtUtils.getUsernameFromToken(token);

        if (managerService.removeByTele(telephone)) {
            // 清除管理员列表缓存和对应管理员的缓存
            redisCacheService.delete("managers:all");
            redisCacheService.delete("manager:tele:" + telephone);
            emailService.send(adminEmail, "管理员删除通知", "管理员已删除，删除的管理员手机号为:" + telephone + ",操作的管理员为:" + getCurrentManager);
            return ApiResult.success("删除成功");
        } else {
            emailService.send(adminEmail, "管理员删除通知", "管理员删除失败，删除的管理员手机号为:" + telephone + ",操作的管理员为:" + getCurrentManager);
            return ApiResult.fail("删除失败");
        }
    }

    /**
     * 退出登录
     */
    @PostMapping("/logout")
    public ApiResult logout(@RequestHeader(value = "Authorization", required = false) String token) {
        try {
            System.out.println("收到退出请求，token: " + (token != null ? token.substring(0, Math.min(20, token.length())) + "..." : "null"));
            
            // 获取当前认证的用户名
            String username = null;
            
            // 方法1：从SecurityContext获取用户名
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null) {
                username = authentication.getName();
                System.out.println("从SecurityContext获取到用户名: " + username);
            }
            
            // 方法2：如果SecurityContext中没有，尝试从token解析
            if ((username == null || "anonymousUser".equals(username)) && token != null && !token.isEmpty()) {
                try {
                    // 确保token格式正确
                    if (token.startsWith("Bearer ")) {
                        token = token.substring(7);
                    }
                    
                    // 尝试从JWT中解析用户名
                    username = jwtUtils.getUsernameFromToken(token);
                    System.out.println("从Token解析到用户名: " + username);
                } catch (Exception e) {
                    // 令牌可能已过期，记录错误但不退出方法
                    System.out.println("Token解析出错，可能已过期: " + e.getMessage());
                }
            }
            
            // 如果找到了用户名，删除Redis中存储的token
            if (username != null && !"anonymousUser".equals(username)) {
                boolean deleted = stringRedisTemplate.delete(username);
                System.out.println("删除用户 " + username + " 的Redis token: " + (deleted ? "成功" : "失败"));
                
                // 也尝试删除以token为键的条目
                if (token != null && !token.isEmpty()) {
                    stringRedisTemplate.delete(token);
                }
            } else {
                System.out.println("未能获取有效的用户名，无法删除Redis中的token");
            }
            
            // 清除安全上下文
            SecurityContextHolder.clearContext();
            
            // 减少在线用户计数
            optimizedCounterService.decrement("online_users");
            
            return ApiResult.success("退出成功");
        } catch (Exception e) {
            System.err.println("退出登录时发生错误: " + e.getMessage());
            e.printStackTrace(); // 打印完整堆栈跟踪以便调试
            // 即使发生错误，也返回成功，因为用户端无法处理退出登录的错误
            return ApiResult.success("退出成功");
        }
    }


    /**
     * 获取在线用户数
     * @return 在线用户数
     */
    @GetMapping("/getOnlineUsers")
    public ApiResult getOnlineUsers() {
        // 1. 定义缓存key
        String cacheKey = "online_users_count";

        // 2. 尝试从缓存获取
        Object cachedCount = redisCacheService.get(cacheKey);
        if (cachedCount != null) {
            System.out.println("从缓存获取在线用户数: " + cachedCount);
            return ApiResult.success(cachedCount);
        }

        // 3. 缓存未命中，查询实际数据
        System.out.println("缓存未命中，从数据源获取在线用户数");
        Long count = optimizedCounterService.getCount("online_users");

        // 4. 将结果存入缓存（设置60秒过期时间）
        redisCacheService.set(cacheKey, count, 60, TimeUnit.SECONDS);

        return ApiResult.success(count);
    }

    @GetMapping("/getManagerCount")
    public ApiResult getManagerCount() {
        // 1. 定义缓存key
        String cacheKey = "manager_count";

        // 2. 尝试从缓存获取
        Object cachedCount = redisCacheService.get(cacheKey);
        if (cachedCount != null) {
            System.out.println("从缓存获取管理员数: " + cachedCount);
            return ApiResult.success(cachedCount);
        }

        // 3. 缓存未命中，查询实际数据
        System.out.println("缓存未命中，从数据源获取管理员数");

        Long count = optimizedCounterService.getCount("managers:all");

        redisCacheService.set(cacheKey, count, 60, TimeUnit.SECONDS);
        return ApiResult.success(count);
    }

    /**
     * 发送邮件
     * @param to 收件人邮箱
     * @param subject 邮件主题
     * @param text 邮件内容
     * @return 发送结果
     */
    @PostMapping("/sendEmail")
    public ApiResult sendEmail(@RequestParam String to,
                               @RequestParam String subject,
                               @RequestParam String text,
                               @RequestHeader("Authorization") String authHeader) {
        try {
            // 直接传递Authorization头的值
            String getCurrentManager = jwtUtils.getUsernameFromToken(authHeader);
            System.out.println("发送邮件的邮箱为:" + getCurrentManager);
            emailService.send(adminEmail, "刚刚没收到吗", "我在用我的这个系统发邮件哈哈哈,正在操作的对象为:" + getCurrentManager);
            return ApiResult.success("邮件发送成功");
        } catch (Exception e) {
            return ApiResult.fail("邮件发送失败: " + e.getMessage());
        }
    }

    /**
     * 定时任务,发送给超级管理员
     * @param
     * @return
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void sendEmailToSuperAdmin() {
        try {

            emailService.send(adminEmail, "刚刚没收到吗", "我在用我的这个系统发邮件哈哈哈,正在操作的对象为:" + "超级管理员");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @GetMapping("/findByTele/{telephone}")
    public ApiResult findByTele(@PathVariable String telephone) {
        // 1. 定义缓存key
        String cacheKey = "manager:tele:" + telephone;

        // 2. 尝试从缓存获取
        Object cachedManager = redisCacheService.get(cacheKey);
        if (cachedManager != null) {
            return ApiResult.success(cachedManager);
        }

        // 3. 检查是否缓存了空值
        if (redisCacheService.exists(cacheKey + ":empty")) {
            return ApiResult.fail("管理员不存在");
        }

        // 4. 缓存未命中，查询数据库
        Optional<Manager> managerOpt = managerService.findByTele(telephone);

        if (managerOpt.isPresent()) {
            Manager manager = managerOpt.get();
            // 创建简化版的管理员对象
            Map<String, Object> simplifiedManager = new HashMap<>();
            simplifiedManager.put("id", manager.getId());
            simplifiedManager.put("name", manager.getName());
            simplifiedManager.put("telephone", manager.getTelephone());
            simplifiedManager.put("email", manager.getEmail());
            simplifiedManager.put("status", manager.getStatus());
            simplifiedManager.put("type", manager.getType());

            // 5a. 缓存简化版的对象（30分钟）
            redisCacheService.set(cacheKey, simplifiedManager, 30, TimeUnit.MINUTES);
            return ApiResult.success(manager); // 返回原始对象
        } else {
            // 5b. 缓存空结果（5分钟）
            redisCacheService.set(cacheKey + ":empty", "null", 5, TimeUnit.MINUTES);
            return ApiResult.fail("管理员不存在");
        }
    }

    /**
     * 将Manager对象转换为简化版Map（不包含authorities）
     */
    private Map<String, Object> simplifyManager(Manager manager) {
        Map<String, Object> map = new HashMap<>();
        map.put("id", manager.getId());
        map.put("name", manager.getName());
        map.put("telephone", manager.getTelephone());
        map.put("email", manager.getEmail());
        map.put("status", manager.getStatus());
        map.put("type", manager.getType());
        return map;
    }

}
